/*
 * Copyright (C) 2011 Lavettacn Xiao (lavettacn@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lavettacn.android.app.sociallessness.data;

import java.util.ArrayList;

import com.lavettacn.android.app.sociallessness.data.table.BaseTable;
import com.lavettacn.android.app.sociallessness.data.table.TableElement;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.text.TextUtils;

public abstract class DataContentProvider extends ContentProvider {

    private static final String TAG = "DataContentProvider";

    protected SQLiteOpenHelper mDatabase = null;
    private final ArrayList<Mapping> mMappings = new ArrayList<Mapping>();
    private final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int match = mUriMatcher.match(uri);
        if (match == UriMatcher.NO_MATCH) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        Mapping mapping = mMappings.get(match);
        if (mapping.mHasId) {
            selection = whereWithId(uri, selection);
        }

        SQLiteDatabase db = mDatabase.getWritableDatabase();
        String tableName = mapping.mTable.getTableName();
        int count = db.delete(tableName, selection, selectionArgs);
        notifyChange(uri);
        return count;
    }

    @Override
    public String getType(Uri uri) {
        int match = mUriMatcher.match(uri);
        if (match == UriMatcher.NO_MATCH) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        Mapping mapping = mMappings.get(match);
        String prefix = mapping.mHasId ? ContentResolver.CURSOR_ITEM_BASE_TYPE : ContentResolver.CURSOR_DIR_BASE_TYPE;
        return prefix + "/" + mapping.mMimeSubtype;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        int match = mUriMatcher.match(uri);
        Mapping mapping = match != UriMatcher.NO_MATCH ? mMappings.get(match) : null;
        if (mapping == null || mapping.mHasId) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        String tableName = mapping.mTable.getTableName();
        long rowId = mDatabase.getWritableDatabase().insert(tableName, TableElement.ID,
                values);
        if (rowId > 0) {
            notifyChange(uri);
            return Uri.withAppendedPath(uri, Long.toString(rowId));
        } else {
            throw new SQLException("Failed to insert row at: " + uri);
        }
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] values) {
        int match = mUriMatcher.match(uri);
        Mapping mapping = match != UriMatcher.NO_MATCH ? mMappings.get(match) : null;
        if (mapping == null || mapping.mHasId) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        String tableName = mapping.mTable.getTableName();
        SQLiteDatabase database = mDatabase.getWritableDatabase();
        int numInserted = 0;
        try {
            int length = values.length;
            database.beginTransaction();
            for (int i = 0; i != length; ++i) {
                database.insert(tableName, TableElement.ID, values[i]);
            }
            database.setTransactionSuccessful();
            numInserted = length;
        } finally {
            database.endTransaction();
        }
        notifyChange(uri);
        return numInserted;
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        int match = mUriMatcher.match(uri);
        if (match == UriMatcher.NO_MATCH) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        Mapping mapping = mMappings.get(match);
        if (mapping.mHasId) {
            selection = whereWithId(uri, selection);
        }

        String tableName = mapping.mTable.getTableName();
        Cursor cursor = mDatabase.getReadableDatabase().query(tableName, projection, selection, selectionArgs, null, null,
                sortOrder);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        int match = mUriMatcher.match(uri);
        if (match == UriMatcher.NO_MATCH) {
            throw new IllegalArgumentException("Invalid URI: " + uri);
        }

        Mapping mapping = mMappings.get(match);
        if (mapping.mHasId) {
            selection = whereWithId(uri, selection);
        }

        SQLiteDatabase db = mDatabase.getWritableDatabase();
        String tableName = mapping.mTable.getTableName();
        int count = db.update(tableName, values, selection, selectionArgs);
        notifyChange(uri);
        return count;
    }

    public void setDatabase(SQLiteOpenHelper database) {
        mDatabase = database;
    }

    public SQLiteOpenHelper getDatabase() {
        return mDatabase;
    }

    public void addMapping(String authority, String path, String mimeSubtype, BaseTable table) {
        ArrayList<Mapping> mappings = mMappings;
        UriMatcher matcher = mUriMatcher;
        matcher.addURI(authority, path, mappings.size());
        mappings.add(new Mapping(table, mimeSubtype, false));

        matcher.addURI(authority, path + "/#", mappings.size());
        mappings.add(new Mapping(table, mimeSubtype, true));
    }

    private static final class Mapping {
        public BaseTable mTable;
        public String mMimeSubtype;
        public boolean mHasId;

        public Mapping(BaseTable table, String mimeSubtype, boolean hasId) {
            mTable = table;
            mMimeSubtype = mimeSubtype;
            mHasId = hasId;
        }
    }

    private final String whereWithId(Uri uri, String selection) {
        String id = uri.getPathSegments().get(1);
        StringBuilder where = new StringBuilder("_id=");
        where.append(id);
        if (!TextUtils.isEmpty(selection)) {
            where.append(" AND (");
            where.append(selection);
            where.append(')');
        }
        return where.toString();
    }

    private final void notifyChange(Uri uri) {
        getContext().getContentResolver().notifyChange(uri, null);
    }

}
